package com.surrey.p2p.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.surrey.p2p.model.CANNode;
import com.surrey.p2p.model.CANZone;
import com.surrey.p2p.model.P2PNode;

public class XMLInputReader {
	private DocumentBuilderFactory dbf;
	private DocumentBuilder db;
	private Document dom;

	public XMLInputReader(String uri) {
		dbf = DocumentBuilderFactory.newInstance();
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		try {
			dom = db.parse(uri);
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public List<P2PNode> getCANNodes()throws Exception{
		List<P2PNode> nodes = new ArrayList<P2PNode>();
		NodeList nodeList = dom.getElementsByTagName("CANNode");
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node node = nodeList.item(i);
			nodes.add(getCANNode(node));
		}
		NodeList neighboursLists = dom.getElementsByTagName("CANNeighbourList");
		for (int i = 0; i < neighboursLists.getLength(); i++) {
			Node node = neighboursLists.item(i);
			assignNeighbours(nodes, node);
		}
		return nodes;
	}

	private void assignNeighbours(List<P2PNode> nodes, Node node) {
		Node addressN = getChildNodeByName(node, "NodeAddress");
		String address=null;
		if(addressN!=null){
		address=addressN.getTextContent();
		CANNode sourceNode = findNodeWithAddress(address, nodes);
		List<String> neighbourAddresses = getAddresses(getChildNodeByName(node, "Neighbours"));
		List<CANNode> neighbours = new ArrayList<CANNode>();
		for (String s : neighbourAddresses) {
			neighbours.add(findNodeWithAddress(s, nodes));
		}
		sourceNode.setNeighbours(neighbours);
		}
	}

	private CANNode findNodeWithAddress(String address, List<P2PNode> nodes) {
		CANNode c = null;
		for (P2PNode n : nodes) {
			if (n.getAddress().getIpAddress().equalsIgnoreCase(address)) {
				c = (CANNode) n;
				break;
			}
		}
		return c;
	}

	private List<String> getAddresses(Node neighboursNode) {
		List<Node> neighbours = getChildrenNodeByName(neighboursNode, "Neighbour");
		List<String> neighbourAddresses = new ArrayList<String>();
		for (int i = 0; i < neighbours.size(); i++) {
			neighbourAddresses.add(neighbours.get(i).getTextContent());
		}
		return neighbourAddresses;
	}

	private CANNode getCANNode(Node node) throws Exception {
		CANNode canNode = null;
		String address = getAddress(node);
		CANZone zone = getCANZone(node);
		canNode = new CANNode(address, zone, null, null);
		return canNode;
	}

	private Node getChildNodeByName(Node parent, String name) {
		Node child = null;
		NodeList children = parent.getChildNodes();
		for (int i = 0; (i < children.getLength() && child == null); i++) {
			Node n = children.item(i);
			if (n.getNodeName().equalsIgnoreCase(name)) {
				child = n;
			}
		}
		return child;
	}
	
	private List<Node> getChildrenNodeByName(Node parent, String name) {
		List<Node> childList = new ArrayList<Node>();
		NodeList children = parent.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node n = children.item(i);
			if (n.getNodeName().equalsIgnoreCase(name)) {
				childList.add(n);
			}
		}
		return childList;
	}

	private CANZone getCANZone(Node node) throws Exception {
		Node canZoneNode = getChildNodeByName(node, "CANZone");
		double minX = Double.parseDouble(getChildNodeByName(canZoneNode, "minX").getTextContent());
		double maxX = Double.parseDouble(getChildNodeByName(canZoneNode, "maxX").getTextContent());
		double minY = Double.parseDouble(getChildNodeByName(canZoneNode, "minY").getTextContent());
		double maxY = Double.parseDouble(getChildNodeByName(canZoneNode, "maxY").getTextContent());
		return new CANZone(minX, maxX, minY, maxY);
	}

	private String getAddress(Node node) {
		return getChildNodeByName(node, "NodeAddress").getTextContent();
	}
}
